ECMAScript 6 入门(阮一峰)笔记

  • cindy Liu
  • 26 Minutes
  • November 1, 2018



最近在读阮一峰的《ECMAScript 6 入门》,之前接触es6只知道一些常见的特性,原理并不深入,通过这本书收获很多。之前网上看到过一个题目,问到使用const来定义对象会发生什么,能不能改变值?最初看到这个题目的时候,第一反应就是const是用来定常量的,定义了就不能改变,非常的想当然,但是并没有深入了解原理,现在重新阅读了这本书之后,发现以前的理解有许多偏差,因此也收获了很多,以下是一些读书笔记,将陆续更新

1、babel-polyfill

Bable默认只转换新的javascript语法,而不转换新的API,比如iterator、gennerator、set、map、proxy、reflect、symbol、promise等全局对象。
举例来说,ES6在Array对象上新增了Array.from方法,babel就不会转换这个方法,需要通过bable-polyfill来进行转换

2、ESLint

用于静态检查代码的语法和风格

3、let

1
2
3
4
5
6
7
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6

在var情况下,变量在声明之前使用,虽然值为undefined,但是不会报错,但是由于let不存在变量提升,因此会报错

1
2
3
4
5
6
7
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

只要块级作用域内存在let命令,他所声明的变量就绑定在这个区域,不受外部影响

1
2
3
4
5
6
var tmp = 123;

if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}

const

对于简单类型的数据(数值、字符串、布尔值),值就保存在指向的内存地址,因此等同于常量。

但对于复合类型的数据(主要是对象和数组),指向的只是一个指向实际数据的指针,const只能保证这个指针固定不懂,至于指向的数据结构是不是可变,就不能控制了

1
2
3
4
5
6
7
8
const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

若真的想将对象冻结,应该使用Object.freeze()

声明变量的六种方法

var function let const import class

顶层对象的属性

顶层对象,在浏览器环境指的是window对象,在node指的是global对象

ES5中,顶层对象和全局变量是等价的,而在ES6中,let、const、class声明的全局变量,不属于顶层对象的属性。

变量的结构赋值

从数组和对象中提取值,对变量进行赋值,被称之为解构。只要等号两边的模式相同,左边的变量就会被赋予对应的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

let [x, , y] = [1, 2, 3];
x // 1
y // 3

let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]

let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []

只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。

字符串的扩展

ES6为字符串添加了遍历器接口,是的字符可以被for ..of 循环遍历

1
2
3
4
5
6
for(let codePoint of 'foo'){
console.log(condePoint)
}
// f
// o
// o
includes(), startsWith(), endsWith()

传统上,只有indexOf方法可以用来判断一个字符串是否包含另一个字符串。es6提供了以下三种新方法

repeat()

repeat()返回一个新字符串,表示将源字符串重复n次,只针对字符串做操作!

1
'x'.repeat(3) //xxx

模板字符串

1
2
3
let name ='cindy'
let a = `my name is ${name}`
console.log(a) //my name is cindy

若使用模板字符串表示多行字符串,所有的空格和缩进都会被保留在输出之中

字符串模板中还能调用函数

1
2
3
4
5
function fn(){
return "Hello world"
}

`foo ${fn()} bar` //"foo Hello world bar"

大括号内可以放任意表达式、函数、变量

正则的扩展

数值的扩展

函数的扩展

函数参数允许指定默认值
1
2
3
4
5
6
function log(x,y = 'world'){
console.log(x,y);
}
log('Hello') // "Hello world"
log('Hello','cindy') // "Hello cindy"
log('Hello','') // "Hello"
函数的length属性

返回没有置顶默认值的参数个数

1
2
(function (a){}).length //1
(function (a=1){}),length //0

箭头函数

可以使用一个圆括号代表参数部分

1
2
3
4
5
6
7
8
9
10
11
var f = () =>5
//相当于
var f = function (){
return 5
}

var sum = (num1,num2)=>num1+num2;
//等同于
var sum = function (num1,num2){
return num1+num2;
}

箭头函数的使用注意点

==this对象的指向是可变的,但是在箭头函数中,他是固定的==

箭头函数根本没有自己的this,导致内部this就是外层代码块的this,正是因为没有this, 所以不能用作构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}

// 等同于 ES5
function foo() {
var _this = this;

setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
//转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的this,而是引用外层的this。

请问下面的代码之中有几个this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id);
};
};
};
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
双冒号运算符

由于箭头函数并不适用于所有场合,因此“函数绑定”运算符,用来取代call、bind、apply调用

函数绑定运算符是并排的两个冒号(::),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即this对象),绑定到右边的函数上面。

1
2
3
4
5
6
7
8
9
10
11
12
foo::bar;
// 等同于
bar.bind(foo);

foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);

const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
代替函数的apply方法

由于扩展运算符可以展开数组,因此不再需要apply方法,下面是利用扩展运算符求一个数组最大元素的例子

1
2
3
4
5
6
7
8
//es5
Math.max.apply(null,[1,23,4,5,3])

//es6
Math.max(...[1,23,4,5,3])

//等同于
Math.max(1,23,4,5,3)

扩展运算符的应用
Array.from()

Array.from 方法用于将两类对象转为真正的数组。

1
2
3
4
5
6
7
8
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};

let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

Array.of()

Array.of()用于将一组数值,转换为数组

对象的扩展

属性的便利
super 关键字

指向当前对象的原型对象

对象新增的方法

Object.is()

用来比较两个值是否严格相等。

1
2
3
4
5
6
7
8
9
10
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false

+0 === -0 //true
NaN === NaN // false

Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
Object.assign()

Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。

1
2
3
4
5
6
7
const target = { a: 1, b: 1 };

const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };

Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Set

类似于数组,但是成员的值都是唯一的,因此可以用来去重

1
[...new Set(array)]

后续添加如下笔记

promise

Iterator

async 函数

class

ES6面试